﻿namespace TSP.Algorithm;

internal class Tour
{
    public Tour(List<City> cities)
    {
        CitySeries = cities;
        Distance = CalcDist();
        Fitness = CalcFit();
    }

    private Random R { get; set; } = new Random();

    internal List<City> CitySeries { get; private set; }

    internal byte[] CityIDSeries
    {
        get
        {
            byte[] result = new byte[CitySeries.Count];
            for (int i = 0; i < CitySeries.Count; i++)
            {
                result[i] = CitySeries[i].CityID;
            }
            return result;
        }
    }

    internal double Distance { get; private set; }

    internal double Fitness { get; private set; }

    internal Tour Shuffle()
    {
        var tmp = new List<City>(CitySeries);
        int n = tmp.Count;
        while (n > 1)
        {
            n--;
            int k = R.Next(n + 1);
            (tmp[n], tmp[k]) = (tmp[k], tmp[n]);
        }
        return new Tour(tmp);
    }

    internal Tour Crossover(Tour m)
    {
        int i = R.Next(0, m.CitySeries.Count);
        int j = R.Next(i, m.CitySeries.Count);
        List<City> s = CitySeries.GetRange(i, j - i + 1);
        List<City> ms = m.CitySeries.Except(s).ToList();
        List<City> c = ms.Take(i)
                         .Concat(s)
                         .Concat(ms.Skip(i))
                         .ToList();
        return new Tour(c);
    }

    internal Tour Mutate()
    {
        var tmp = new List<City>(CitySeries);
        if (R.NextDouble() < TSPCalculator.MutRate)
        {
            int i = R.Next(0, CitySeries.Count);
            int j = R.Next(0, CitySeries.Count);
            (tmp[j], tmp[i]) = (tmp[i], tmp[j]);
        }
        return new Tour(tmp);
    }

    private double CalcDist()
    {
        double total = 0;
        for (int i = 0; i < CitySeries.Count; ++i)
            total += CitySeries[i].DistanceTo(CitySeries[(i + 1) % CitySeries.Count]);
        return total;
        //使用Linq会使执行时间加倍
        //return  t.Sum( c => c.distanceTo( t[ ( t.IndexOf(c) + 1) %  t.Count] ) );
    }

    private double CalcFit() => 1 / Distance;

}